package org.moonlight.jvm.instructions.base;

import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.exclusive.Slot;
import org.moonlight.jvm.rtda.exclusive.Thread;
import org.moonlight.jvm.rtda.share.heap.classmember.Method;

/**
 * 方法执行通用逻辑
 *
 * 在定位到需要调用的方法之后，Java虚拟机要给这个方法创建一个新的帧并把它推入Java虚拟机栈顶，然后传递参数。
 * 注意，这个数量并不一定等于从Java代码中看到的参数个数，原因有两个。
 *  1. long和double类型的参数要占用两个位置。
 *  2，对于实例方法，Java编译器会在参数列表的前面添加一个参数，
 *    这个隐藏的参数就是this引用。
 *
 * 假设实际的参数占据n个位置，依次把这n个变量从调用者的操作数栈中弹出，放进被调用方法的局部变量表中，参数传递就完成了。
 *
 * @author Moonlight
 * @createTime 2023/9/12 10:45
 **/
public abstract class MethodInvokeLogic extends Index16Instruction {

    public static void invokeMethod(Frame invokeFrame, Method method) {
        // 前三行代码创建新的帧并推入Java虚拟机栈，
        Thread thread = invokeFrame.getThread();
        Frame frame = thread.newFrame(method);
        thread.pushFrame(frame);
        // 获取参数，确定参数在在局部变量表中中占用了多少个位置
        int argSlotCount = method.getArgSlotCount();
        if (argSlotCount > 0) {
            for (int i = argSlotCount - 1; i >= 0; i--) {
               Slot slot = invokeFrame.getOperandStack().popSlot();
               frame.getLocalVars().setSlot(i, slot);
            }
        }
        // 目前还不支持本地方法调用，而Java类库中的很多类都要注册本地方法，比如Object类就有一个registerNatives()本地方法，用于注册其他方法
        // 由于Object类是其他所有类的超类，所以这会导致Java虚拟机崩溃。所以我们在这里让它跳过所有registerNatives()方法
//        if (method.isNative()) {
//            if ("registerNatives".equals(method.getName())) {
//                thread.popFrame();
//            } else {
//                throw new RuntimeException("暂不支持本地方法调用 native method: "
//                        + method.getClazz().getName()
//                        + "." + method.getName() + method.getDescriptor());
//            }
//        }

    }

}
